package xyz.thoughtset.viewer.executor.core.base;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.executor.core.entity.QueryBody;
import xyz.thoughtset.viewer.executor.core.factory.ExecutorRegistry;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public abstract class AbstractExecutor {
    protected TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final String head_xml = "<root>";
    protected final String tail_xml = "</root>";
    @Getter
    protected final String supportType;
    @Getter
    protected final boolean innerExecutor;
    @Autowired
    protected ObjectMapper objectMapper;


    protected AbstractExecutor(String supportType, boolean innerExecutor){
        this.supportType = supportType;
        this.innerExecutor = innerExecutor;
        ExecutorRegistry.register(this);
    }

    protected XNode buildRootNode(String baseTest){
        baseTest = baseTest.trim().replaceAll("&", "&amp;");
        //todo:优化点直接使用DocumentBuilderFactory进行创建
        XPathParser parser = new XPathParser(head_xml+baseTest+tail_xml);
        XNode rootNode = parser.evalNode("/root");
        return rootNode;
    }

    protected DataNode buildResultTree(XNode resultMapNode){
        if (resultMapNode==null) return null;
        return buildResultTree(resultMapNode,new DataNode());
    }
    protected DataNode buildResultTree(XNode resultMapNode,DataNode parentDataNode){
        List<XNode> resultChildren = resultMapNode.getChildren();
        boolean hasConfirmKey = false;
//        String keyColumns = resultMapNode.getStringAttribute("idColumns");
//        if (StringUtils.hasText(keyColumns)){
//            String[] keyArr = keyColumns.split(",");
//            for (String k : keyArr) {
//                parentDataNode.putKeyClew(k);
//            }
//            hasConfirmKey = true;
//        }
        for (XNode resultChild : resultChildren) {
            String nodeName = resultChild.getName();
            String property =  resultChild.getStringAttribute("property");
            int nodeType = 0;
            switch (nodeName) {
                case "collection" :
                    nodeType = 2;
                    break;
                case "association" :
                    nodeType = 1;
                    break;
            }
            if (nodeType>0){
                DataNode sonDataNode = new DataNode();
                sonDataNode.setNodeKey(property);
                sonDataNode.setNodeType(nodeType);
                parentDataNode.putSonDataNode(sonDataNode);
                buildResultTree(resultChild,sonDataNode);
            }
            if (hasConfirmKey) continue;
            if ("id".equals(resultChild.getName())) {
                parentDataNode.putKeyClew(property);
                hasConfirmKey = true;
            }else {
                parentDataNode.putColumnClew(property);
            }
        }
        return parentDataNode;
    }


    protected JdbcType resolveJdbcType(String alias) {
        try {
            return alias == null ? null : JdbcType.valueOf(alias);
        } catch (IllegalArgumentException e) {
            throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
        }
    }

    protected Class resolveJavaType(String alias) {
        try {
            return !StringUtils.hasText(alias) ? String.class : typeAliasRegistry.resolveAlias(alias);
        } catch (IllegalArgumentException e) {
            log.info("Error resolving JdbcType. Cause: " + e, e);
            return String.class;
        }
    }

    public Object execute(QueryBody queryBody, Map param){
        return exec(queryBody,param);
    }

    abstract protected Object exec(QueryBody queryBody,Map dataContext);

}
